<?php

/**
 * 安心站长axzz.cn，一个追求技术创造者。让网站、小程序、APP、应用程序牛起来……
 *
 * cighsen02 qq 365182575
 *
 * Create at: 2022.09.29
 * Update at: 2025.09.16
 */

namespace Twoir\System\Support;

use Exception;

use function array_filter;
use function array_intersect;
use function array_replace;
use function array_values;
use function DelTxtFieldText;
use function eCheckStrType;
use function EditTxtFieldText;
use function GetTxtFieldText;
use function implode;
use function in_array;
use function MkDirTxtFile;
use function sprintf;
use function str_contains;
use function str_replace;

/**
 * 帝国模型表Trait
 */
trait ModelTrait
{
    /**
     * 字段默认值类型
     *
     * @return array
     */
    public static function fieldType(): array
    {
        $default = array_replace(static::MYDEFAULT['index'], static::MYDEFAULT['pri'], static::MYDEFAULT['sec']);
        // 字段类型
        foreach($default as &$value) {

            $value = null === $value || 0 === $value ? 'int' : 'string';
        }

        return $default;
    }

    /**
     * 分组表单名
     *
     * @param string $type
     * @return array
     */
    private static function parseTable(string $type = ''): array
    {
        $index 	= 'doc' === $type ? 'doc_index' : 'index';
        $pre	= in_array($type, ['doc', 'check']) ? $type . '_' : '';

        return [
            'index' => static::MPREFIX . static::MODEL . static::TABLE[$index],
            'pri'   => static::MPREFIX . static::MODEL . static::TABLE[$pre . 'pri'],
            'sec'   => static::MPREFIX . static::MODEL . static::TABLE[$pre . 'sec'],
        ];
    }

    /**
     * 统计审核数量
     *
     * @param array $where
     * @param string $field
     * @param string $type empty|doc|check
     * @return int
     */
    public static function getCount(array $where = [], string $field = '', string $type = ''): int
    {
        global $empire, $dbtbpre;

        list($str_where) = static::parseWhere($where);

        $table = static::parseTable($type);
        // 位置表位置
        $index = 'all' === $field ? 'index' : 'pri';

        $sql = sprintf('SELECT count(*) as total FROM `%s`%s', $dbtbpre . $table[$index], $str_where);

        return (int)$empire->gettotal($sql);
    }

    /**
     * 统计待审核数
     *
     * @param array $where
     * @param string $field
     * @return int
     */
    public static function getCheckCount(array $where = [], string $field = ''): int
    {
        return static::getCount($where, $field, 'check');
    }

    /**
     * 存档
     *
     * @param array $where
     * @param string $field
     * @return int
     */
    public static function getDocCount(array $where = [], string $field = ''): int
    {
        return static::getCount($where, $field, 'doc');
    }

    /**
     * 拉取一条数据
     *
     * @param array $where 键值数组 $field 值数组
     * @param array $field
     * @param string $groupby
     * @param string $orderby
     * @param string $type
     * @return array
     */
    public static function getOne(array $where = [], array $field = ['*'], string $groupby = '', string $orderby = '', string $type = ''): array
    {
        $result = static::getList($where, $field, $groupby, $orderby, 1, 0, 0, $type);

        return $result ? array_values($result)[0] : [];
    }

    /**
     * 待审核
     *
     * @param array $where 键值数组 $field 值数组
     * @param array $field
     * @param string $groupby
     * @param string $orderby
     * @return array
     */
    public static function getCheckOne(array $where = [], array $field = ['*'], string $groupby = '', string $orderby = ''): array
    {
        $result = static::getList($where, $field, $groupby, $orderby, 1, 0, 0, 'check');

        return $result ? array_values($result)[0] : [];
    }

    /**
     * 存档
     *
     * @param array $where 键值数组 $field 值数组
     * @param array $field
     * @param string $groupby
     * @param string $orderby
     * @return array
     */
    public static function getDocOne(array $where = [], array $field = ['*'], string $groupby = '', string $orderby = ''): array
    {
        $result = static::getList($where, $field, $groupby, $orderby, 1, 0, 0, 'doc');

        return $result ? array_values($result)[0] : [];
    }

    /**
     * 待审核表
     *
     * @param array $where 键值数组 $field 值数组
     * @param array $field
     * @param string $groupby
     * @param string $orderby
     * @param int $limit
     * @param int $pagesize
     * @param int $page
     * @return array
     */
    public static function getCheckList(array $where = [], array $field = ['*'], string $groupby = '', string $orderby = '', int $limit = 0, int $pagesize = 10, int $page = 0): array
    {
        return static::getList($where, $field, $groupby, $orderby, $limit, $pagesize, $page, 'check');
    }

    /**
     * 存档
     *
     * @param array $where 键值数组 $field 值数组
     * @param array $field
     * @param string $groupby
     * @param string $orderby
     * @param int $limit
     * @param int $pagesize
     * @param int $page
     * @return array
     */
    public static function getDocList(array $where = [], array $field = ['*'], string $groupby = '', string $orderby = '', int $limit = 0, int $pagesize = 10, int $page = 0): array
    {
        return static::getList($where, $field, $groupby, $orderby, $limit, $pagesize, $page, 'doc');
    }

    /**
     * 读取多条(列表)
     *
     * @param array $where
     * @param array $field
     * @param string $groupby
     * @param string $orderby
     * @param int $limit
     * @param int $pagesize
     * @param int $page
     * @param string $type
     * @return array
     * 
     * @throws Exception
     */
    public static function getList(array $where = [], array $field = ['*'], string $groupby = '', string $orderby = '', int $limit = 0, int $pagesize = 10, int $page = 0, string $type = ''): array
    {
        global $empire, $dbtbpre, $etable_r, $emod_r;

        list($str_where, $data) = static::parseWhere($where);
        // 确定表位置正式表、待审核表、存档表
        $tables 	= static::parseTable($type);
        $get_field  = [];
        $mid 		= $etable_r[static::MODEL]['mid'] ?? null;
        // 存文本字段
        $savetxtf 	= $mid && isset($emod_r[$mid]['savetxtf']) ? $emod_r[$mid]['savetxtf'] : null;
        $arr_where 	= [
            'index'		=> [],
            'pri'		=> [],
            'sec'		=> []
        ];

        if (['*'] !== $field && count($data) > 0) {

            $where_field	= array_keys($data);
            $arr_where		= [
                'index'	=> array_filter(static::FIELD['index'], fn($item) => in_array($item, $where_field)),
                'pri'	=> array_filter(static::FIELD['pri'], fn($item) => in_array($item, $where_field)),
                'sec'	=> array_filter(static::FIELD['sec'], fn($item) => in_array($item, $where_field))
            ];
        }

        $str_group	= null === $groupby || '' === $groupby ? '' : ' GROUP BY ' . $groupby;
        $str_order	= null === $orderby || '' === $orderby ? '' : ' ORDER BY ' . $orderby;
        $str_limit 	= 0 === $limit && 0 === $pagesize ? '' : ($limit > 0 ? ' LIMIT ' . $limit : ' LIMIT ' . ($page * $pagesize) . ',' . $pagesize);

        $option = [];
        $valid_fields = [['classid'], ['id']];
        $get_field_is_empty = true;
        foreach($tables as $t => $table) {

            $get_field = ['*'] === $field ? static::FIELD[$t] : array_intersect(static::FIELD[$t], $field);
            $get_where = $arr_where[$t];
            $count = count($get_field);

            if (
                ['*'] === $field
                || $count > 2
                || (2 === $count && 'pri' === $t)
                || (1 === $count && 'pri' === $t && in_array(array_values($get_field), $valid_fields, true))
                || (1 === $count && !in_array(array_values($get_field), $valid_fields, true))
                || (count($get_where) > 0 && !in_array(array_values($get_where), [['classid'], ['id'], ['classid', 'id'], ['id', 'classid']], true))
            ) {

                $option[$t] = [
                    'field'     => $get_field,
                    'table'     => $dbtbpre . $table,
                    'where'     => $str_where,
                    'group'		=> $str_group,
                    'order'     => $str_order,
                    'limit'     => $str_limit
                ];

                if ($get_field_is_empty) $get_field_is_empty = false;
            }
        }

        if ($get_field_is_empty && count($field) > 0) throw new Exception('字段不存在', 9200);

        // 待返回数据
        $result = [];
        // 主表
        if (isset($option['pri']) && !isset($option['sec'])) {

            $sql = sprintf(
                'SELECT %s FROM `%s`%s%s%s%s',
                '`' . implode('`,`', $option['pri']['field']) . '`',
                $option['pri']['table'],
                $option['pri']['where'],
                $option['pri']['group'],
                $option['pri']['order'],
                $option['pri']['limit']
            );
        }

        // 副表, 注意 分表不设置
        elseif (!isset($option['pri']) && isset($option['sec'])) {

            $sql = sprintf(
                'SELECT %s FROM `%s`%s%s%s%s',
                '`' . implode('`,`', $option['sec']['field']) . '`',
                '' === $type ? $option['sec']['table'] . ('' === $type ? static::$stb : '') : $option['sec']['table'],
                $option['sec']['where'],
                $option['sec']['group'],
                $option['sec']['order'],
                $option['sec']['limit']
            );
        }

        // 并表
        else{

            $option['sec']['field'] = array_diff($option['sec']['field'], ['classid', 'id']);
            $sql = sprintf(
                'SELECT %s FROM `%s` AS a INNER JOIN `%s` AS b ON %s%s%s%s%s',
                ($option['pri']['field'] ? 'a.`' . implode('`,a.`', $option['pri']['field']) . '`' : '') . ($option['pri']['field'] && $option['sec']['field'] ? ',' : '') . ($option['sec']['field'] ? 'b.`' . implode('`,b.`', $option['sec']['field']) . '`' : ''),
                $option['pri']['table'],
                '' === $type ? $option['sec']['table'] . ('' === $type ? static::$stb : '') : $option['sec']['table'],
                'a.`' . static::PRIMARY . '` = b.`' . static::PRIMARY . '`',
                count($data) > 0 ? static::addPrefix($option['pri']['where']) : '',
                $option['pri']['group'] ? static::addPrefix($option['pri']['group']) : '',
                $option['pri']['order'] ? static::addPrefix($option['pri']['order']) : '',
                $option['pri']['limit']
            );
        }

        $sql = $empire->query($sql);

        if(0 < $empire->num1($sql)){

            while($r = $empire->fetch($sql)){
                // 存文本处理，替换真实内容
                if (static::$savetxt && $savetxtf && isset($r[$savetxtf])) $r[$savetxtf] = GetTxtFieldText($r[$savetxtf]);
                // 去数字键
                $result[] = array_filter($r, fn($key) => is_string($key), ARRAY_FILTER_USE_KEY);
            }
        }

        return $result;
    }

    /**
     * 插入审核表
     *
     * @param array $add
     * @return int
     */
    public static function insertCheck(array $add = []): int
    {
        return static::insert($add, 'check');
    }

    /**
     * 插入存档表
     *
     * @param array $add
     * @return int
     */
    public static function insertDoc(array $add = []): int
    {
        return static::insert($add, 'doc');
    }

    /**
     * 插入信息
     *
     * @param array $add
     * @param string $type
     * @return int 0 为失败，成功返回当前id
     */
    public static function insert(array $add = [], string $type = ''): int
    {
        global $empire, $dbtbpre, $etable_r, $emod_r;
        // 确定表位置
        $tables     = static::parseTable($type);
        $mid 		= $etable_r[static::MODEL]['mid'] ?? null;
        // 存文本字段名
        $savetxtf 	= $mid && isset($emod_r[$mid]['savetxtf']) ? $emod_r[$mid]['savetxtf'] : null;
        // 存文件开始
        if ($savetxtf && isset($add[$savetxtf])) {
            // 建立目录
            $truevalue = MkDirTxtFile(date("Y/md"), GetFileMd5());
            // 写放文件
            EditTxtFieldText($truevalue, $add[$savetxtf]);
            // 返回存文本路径
            $add[$savetxtf] = $truevalue;
        }

        $insert = static::MYDEFAULT;
        array_walk($insert, function(&$value, $key) use($add) {
            // 分组
            $value  = array_filter($value, fn($item) => in_array($item, static::FIELD[$key]), ARRAY_FILTER_USE_KEY);
            $add    = array_filter($add, fn($item) => in_array($item, static::FIELD[$key]), ARRAY_FILTER_USE_KEY);
            // 更新
            $value 	= array_replace($value, $add);
        });

        // 移除id, 自动增长
        if (!isset($add[static::PRIMARY])) unset($insert['index'][static::PRIMARY]);

        // 确定副表位置
        if ($insert['pri']['stb'] > 0 && static::$stb !== $insert['pri']['stb']) static::$stb = $insert['pri']['stb'];

        // 引索表插入
        list($key, $value) = static::parseInsert($insert['index']);
        $sql        = sprintf('INSERT %sINTO %s(%s) VALUES(%s)', static::$low_priority ? 'LOW_PRIORITY ' : '', $dbtbpre . $tables['index'], $key, $value);
        $sqlindex   = $empire->query($sql);

        $insert['pri']['id'] = $insert['sec']['id'] = $lastid = $empire->lastid();

        // 主表
        list($key, $value) = static::parseInsert($insert['pri']);
        $sql    = sprintf('INSERT %sINTO %s(%s) VALUES(%s)', static::$low_priority ? 'LOW_PRIORITY ' : '', $dbtbpre . $tables['pri'], $key, $value);
        $sqlpri = $empire->query($sql);

        // 副表
        list($key, $value) = static::parseInsert($insert['sec']);
        $sql    = sprintf('INSERT %sINTO %s(%s) VALUES(%s)', static::$low_priority ? 'LOW_PRIORITY ' : '', $dbtbpre . $tables['sec'] . ('' === $type ? static::$stb : ''), $key, $value);
        $sqlsec = $empire->query($sql);

        return $sqlindex && $sqlpri && $sqlsec ? $lastid : 0;
    }

    /**
     * 更新待审核表
     *
     * @param array $where
     * @param array $add
     * @param int $limit
     * @return bool
     */
    public static function updateCheck(array $where = [], array $add = [], int $limit = 0): bool
    {
        return static::update($where, $add, $limit, 'check');
    }

    /**
     * 更新存档表
     *
     * @param array $where
     * @param array $add
     * @param int $limit
     * @return bool
     */
    public static function updateDoc(array $where = [], array $add = [], int $limit = 0): bool
    {
        return static::update($where, $add, $limit, 'doc');
    }

    /**
     * 更新
     *
     * @param array $where
     * @param array $add
     * @param int $limit
     * @param string $type
     * @return bool
     */
    public static function update(array $where = [], array $add = [], int $limit = 0, string $type = ''): bool
    {
        global $empire, $dbtbpre, $etable_r, $emod_r;

        // 去null值, 0值或负值不能去，
        $add = array_filter($add, fn($item) => null !== $item);

        // 找出要新字段进行更新
        $field = array_keys($add);

        list($str_where) = static::parseWhere($where);

        // 确定表位置
        $tables     = static::parseTable($type);
        $mid        = $etable_r[static::MODEL]['mid'] ?? null;
        // 存文本字段
        $savetxtf   = $mid && isset($emod_r[$mid]['savetxtf']) ? $emod_r[$mid]['savetxtf'] : null;

        if ($savetxtf && $add[$savetxtf]) {
            // 取存在文本路径
            static::$savetxt = false;
            $r = static::getOne($where, [$savetxtf]);
            static::$savetxt = true;
            // 建立目录
            $newstexttxt_r   = explode('/', $r[$savetxtf]);
            eCheckStrType(1, $newstexttxt_r[0], 1);
            eCheckStrType(1, $newstexttxt_r[1], 1);
            eCheckStrType(3, $newstexttxt_r[2], 1);
            $truevalue = MkDirTxtFile($newstexttxt_r[0] . '/' . $newstexttxt_r[1], $newstexttxt_r[2]);
            // 写放文件
            EditTxtFieldText($truevalue, $add[$savetxtf]);
            // 返回路径
            $add[$savetxtf] = $truevalue;
        }

        $limit      = $limit ? ' LIMIT ' . $limit : '';
        $sqlindex   = $sqlpri = $sqlsec = false;

        // 更新引索表
        $update = array_intersect(static::FIELD['index'], $field);
        if (!empty($update)) {

            $put_data   = array_filter($add, fn($item) => in_array($item, $update), ARRAY_FILTER_USE_KEY);
            $sql        = sprintf('UPDATE %s`%s` SET %s%s%s', static::$low_priority ? 'LOW_PRIORITY ' : '', $dbtbpre . $tables['index'], static::parseUpdate($put_data), $str_where, $limit);
            $sqlindex   = $empire->query($sql);
        }

        // 更新主表
        $update = array_intersect(static::FIELD['pri'], $field);
        if (!empty($update)) {

            $put_data   = array_filter($add, fn($item) => in_array($item, $update), ARRAY_FILTER_USE_KEY);
            $sql        = sprintf('UPDATE %s`%s` SET %s%s%s', static::$low_priority ? 'LOW_PRIORITY ' : '', $dbtbpre . $tables['pri'], static::parseUpdate($put_data), $str_where, $limit);
            $sqlpri     = $empire->query($sql);
        }

        // 更新副表
        $update = array_intersect(static::FIELD['sec'], $field);
        if (!empty($update)) {

            $put_data   = array_filter($add, fn($item) => in_array($item, $update), ARRAY_FILTER_USE_KEY);
            $sql        = sprintf('UPDATE %s`%s` SET %s%s%s', static::$low_priority ? 'LOW_PRIORITY ' : '', $dbtbpre . $tables['sec'] . ('' === $type ? static::$stb : ''), static::parseUpdate($put_data), $str_where, $limit);
            $sqlsec     = $empire->query($sql);
        }

        return false !== $sqlindex || false !== $sqlpri || false !== $sqlsec;
    }

    /**
     * 删除待审核表数据
     *
     * @param array $where
     * @param int $limit
     * @param string $orderby
     * @param string $type
     * @return bool
     */
    public static function deleteCheck(array $where = [], int $limit = 0, string $orderby = ''): bool
    {
        return static::delete($where, $limit, $orderby, 'check');
    }

    /**
     * 删除存档表数据
     *
     * @param array $where
     * @param int $limit
     * @param string  $orderby
     * @param string $type
     * @return bool
     */
    public static function deleteDoc(array $where = [], int $limit = 0, string $orderby = ''): bool
    {
        return static::delete($where, $limit, $orderby, 'doc');
    }

    /**
     * 删除数据
     *
     * @param array $where
     * @param int $limit
     * @param string  $orderby
     * @param string $type
     * @return bool
     */
    public static function delete(array $where = [], int $limit = 0, string $orderby = '', string $type = ''): bool
    {
        global $empire, $dbtbpre, $emod_r, $etable_r;

        list($str_where) = static::parseWhere($where);
        $table      = static::parseTable($type);
        $mid        = $etable_r[static::MODEL]['mid'] ?? null;
        // 存文本字段
        $savetxtf   = $mid && isset($emod_r[$mid]['savetxtf']) ? $emod_r[$mid]['savetxtf'] : null;
        // 删除存文本内容
        if ($savetxtf) {
            // 取存在文本路径
            static::$savetxt = false;
            $r = static::getOne($where, [$savetxtf]);
            static::$savetxt = true;
            // 删除文件
            DelTxtFieldText($r[$savetxtf]);
        }

        $limit      = $limit ? ' limit ' . $limit : '';
        $orderby    = null === $orderby || '' === $orderby ? '' : ' ORDER BY ' . $orderby;

        $index = sprintf('DELETE %FROM `%s` %s%s%s', static::$low_priority ? 'LOW_PRIORITY ' : '', $dbtbpre . $table['index'], $str_where, $orderby, $limit);
        $index = $empire->query($index);

        $pri = sprintf('DELETE %sFROM `%s` %s%s%s', static::$low_priority ? 'LOW_PRIORITY ' : '', $dbtbpre . $table['pri'], $str_where, $orderby, $limit);
        $pri = $empire->query($pri);

        $sec = sprintf('DELETE %sFROM `%s` %s%s%s', static::$low_priority ? 'LOW_PRIORITY ' : '', $dbtbpre . $table['sec'], $str_where, $orderby, $limit);
        $sec = $empire->query($sec);

        return $index && $pri && $sec;
    }

    /**
     * 联表名增加前缀
     *
     * @param string $str
     * @return string
     */
    private static function addPrefix(string $str): string
    {
        foreach (static::FIELD['pri'] as $field) {

            if (str_contains($str, '`' . $field . '`')) $str = str_replace('`' . $field . '`', 'a.`' . $field . '`', $str);
        }

        foreach (static::FIELD['sec'] as $field) {

            if (!in_array($field, ['classid','id']) && str_contains($str, '`' . $field . '`')) $str = str_replace('`' . $field . '`', 'b.`' . $field . '`', $str);
        }

        return $str;
    }
}
